﻿ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using NeurOthello.NNetwork;

namespace NeurOthello.Logic
{
    public class MiniMax : Player
    {
        private Othello othello;
        private List<AiMove> lastSearchMoveValues = new List<AiMove>();
        public AiMove[] LastSearchMoveValues { get { return lastSearchMoveValues.ToArray(); } }
        private int lastSearchLeafs = 0;
        private int Depth { get; set; }
        private bool alfabeta = true;
        public bool IsCpu { get { return true; } }
        public int LastSearchLeafs { get { return lastSearchLeafs; } }
        private Heuristic heuristic = new Heuristic(1, 1, 1);

        public MiniMax(Othello othello)
        {
            this.othello = othello;
        }

        public MiniMax(Othello othello, Heuristic heur, int depth)
        {
            this.othello = othello;
            this.Depth = depth;
            heuristic = heur;
        }

        public MiniMax(Othello othello, PlayerSettings set)
        {
            this.othello = othello;
            this.Set = set;
            heuristic = new NNHeuristic(set);
        }

        public Position RunMiniMax(int playerId, bool alfabeta, int depth)
        {
            lastSearchMoveValues.Clear();
            //othello.ClearTranspositions();
            Position[] moves = LookAheadx(depth - 3, playerId);

            lastSearchLeafs = 0;
            Position maxMove = moves[0];
            double alfa = -999990, beta = 9999990;
            for (int i = 0; i < moves.Length; i++)
            {
                Othello newNode = new Bitboard((Bitboard)othello);
                newNode.PlaceMarker(playerId, moves[i]);

                double moveVal;
                if (newNode.PlayerTurn == othello.Opponent)
                    moveVal = Min(newNode, playerId, depth - 1, alfa, beta, alfabeta);
                else if (newNode.PlayerTurn == othello.PlayerTurn)
                    moveVal = Max(newNode, playerId, depth - 1, alfa, beta, alfabeta);
                else
                    moveVal = heuristic.Weights(newNode, playerId);

                lastSearchMoveValues.Add(new AiMove(moves[i], moveVal));
                if (moveVal > alfa)
                {
                    maxMove = moves[i];
                    alfa = moveVal;
                }
            }

            return maxMove;
        }

        private double Max(Othello game, int playerId, int i, double alfa, double beta, bool alfabeta)
        {
            if (i == 0)
            {
                lastSearchLeafs++;
                double val = heuristic.Weights(game, playerId);//game.CountMoves(playerId) - game.CountMoves(Othello.Opponent(playerId));
                //game.AddTranspositionValue(val);
                return val;
            }

            /*double transpos = game.FindTranspositionValue();
            if (transpos != Othello.NullToken && transpos > beta)
                return transpos;*/

            Position[] moves = game.AllMoves(game.PlayerTurn);
            alfa = -9999990;
            if (moves != null)
            {
                foreach (Position move in moves)
                {
                    Othello newNode = new Bitboard((Bitboard)game);
                    newNode.PlaceMarker(game.PlayerTurn, move);
                    double moveVal;
                    if (newNode.PlayerTurn == game.Opponent)
                        moveVal = Min(newNode, playerId, i - 1, alfa, beta, alfabeta);
                    else if (newNode.PlayerTurn == othello.PlayerTurn)
                        moveVal = Max(newNode, playerId, i - 1, alfa, beta, alfabeta);
                    else
                        moveVal = heuristic.Weights(newNode, playerId);


                    //newNode.AddTranspositionValue(moveVal);
                    if (moveVal > alfa)
                    {
                        alfa = moveVal;
                    }
                    if (alfabeta && moveVal > beta)
                        break;
                }
                return alfa;
            }
            else //om Maxspelare inte har något drag får minspelare minimera igen utan något alfavärde.
            {
                return Min(game, playerId, i - 1, beta, alfa, alfabeta);
            }
        }

        private double Min(Othello game, int playerId, int i, double alfa, double beta, bool alfabeta)
        {
            if (i == 0)
            {
                lastSearchLeafs++;
                double val = heuristic.Weights(game, playerId);// game.CountMoves(playerId) - game.CountMoves(Othello.Opponent(playerId));
                //game.AddTranspositionValue(val);
                return val;
            }

            /*double transpos = game.FindTranspositionValue();
            if (transpos != Othello.NullToken && transpos < alfa)
                return transpos;*/

            Position[] moves = game.AllMoves(game.PlayerTurn);
            beta = 9999999;
            if (moves != null)
            {
                foreach (Position move in moves)
                {
                    Othello newNode = new Bitboard((Bitboard)game);
                    newNode.PlaceMarker(game.PlayerTurn, move);

                    double moveVal;
                    if (newNode.PlayerTurn == game.Opponent)
                        moveVal = Max(newNode, playerId, i - 1, alfa, beta, alfabeta);
                    else if (newNode.PlayerTurn == othello.PlayerTurn)
                        moveVal = Min(newNode, playerId, i - 1, alfa, beta, alfabeta);
                    else
                        moveVal = heuristic.Weights(newNode, playerId);

                    //newNode.AddTranspositionValue(moveVal);
                    if (moveVal < beta)
                    {
                        beta = moveVal;
                    }
                    if (alfabeta && moveVal < alfa)
                        break;
                }
                return beta;
            }
            else//om Minspelare inte har något drag får maxspelare maximera igen utan något betavärde.
            {
                return Max(game, playerId, i - 1, beta, alfa, alfabeta);
            }
        }

        public Position[] LookAheadx(int x, int playerId)
        {
            Position[] moves = othello.AllMoves(playerId);
            if (x <= 0)
                return moves;
            List<AiMove> Moves = new List<AiMove>();

            for (int i = 0; i < moves.Length; i++)
            {
                Othello newNode = new Bitboard((Bitboard)othello);
                newNode.PlaceMarker(playerId, moves[i]);
                double moveVal = Min(newNode, playerId, x, 0, 0, false);

                Moves.Add(new AiMove(moves[i], moveVal));
            }
            Moves.Sort();

            return Moves.ToArray();
        }

        #region Player Members

        public Position MakeMove(int playerId)
        {
            return RunMiniMax(playerId, alfabeta, Set.Depth);
        }

        public Position MakeTrainingMove(int playerId)
        {
            return MakeMove(playerId);
        }

        public void TrainPlayer(double[] result) { }

        public PlayerSettings Set { get; set; }

        public void SetGame(Othello game)
        {
            this.othello = game;
        }            

        #endregion
    }
}
